function randomStringFn(length, letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') {
  length || (length = 8);
  const numbers = '0123456789';
  // const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  let total = '';
  total = numbers + letters;
  let result = '';

  while (length > 0) {
    length--;
    result += total[Math.floor(Math.random() * total.length)];
  }

  return result;
}

/**
 * CRC16校验
 * @param data 数据
 */
function crc16Modbus(data) {
  const buffer = Buffer.from(data, 'hex');
  const auchCRCHi = new Array(
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x01,
    0xc0,
    0x80,
    0x41,
    0x00,
    0xc1,
    0x81,
    0x40
  );
  const auchCRCLo = new Array(
    0x00,
    0xc0,
    0xc1,
    0x01,
    0xc3,
    0x03,
    0x02,
    0xc2,
    0xc6,
    0x06,
    0x07,
    0xc7,
    0x05,
    0xc5,
    0xc4,
    0x04,
    0xcc,
    0x0c,
    0x0d,
    0xcd,
    0x0f,
    0xcf,
    0xce,
    0x0e,
    0x0a,
    0xca,
    0xcb,
    0x0b,
    0xc9,
    0x09,
    0x08,
    0xc8,
    0xd8,
    0x18,
    0x19,
    0xd9,
    0x1b,
    0xdb,
    0xda,
    0x1a,
    0x1e,
    0xde,
    0xdf,
    0x1f,
    0xdd,
    0x1d,
    0x1c,
    0xdc,
    0x14,
    0xd4,
    0xd5,
    0x15,
    0xd7,
    0x17,
    0x16,
    0xd6,
    0xd2,
    0x12,
    0x13,
    0xd3,
    0x11,
    0xd1,
    0xd0,
    0x10,
    0xf0,
    0x30,
    0x31,
    0xf1,
    0x33,
    0xf3,
    0xf2,
    0x32,
    0x36,
    0xf6,
    0xf7,
    0x37,
    0xf5,
    0x35,
    0x34,
    0xf4,
    0x3c,
    0xfc,
    0xfd,
    0x3d,
    0xff,
    0x3f,
    0x3e,
    0xfe,
    0xfa,
    0x3a,
    0x3b,
    0xfb,
    0x39,
    0xf9,
    0xf8,
    0x38,
    0x28,
    0xe8,
    0xe9,
    0x29,
    0xeb,
    0x2b,
    0x2a,
    0xea,
    0xee,
    0x2e,
    0x2f,
    0xef,
    0x2d,
    0xed,
    0xec,
    0x2c,
    0xe4,
    0x24,
    0x25,
    0xe5,
    0x27,
    0xe7,
    0xe6,
    0x26,
    0x22,
    0xe2,
    0xe3,
    0x23,
    0xe1,
    0x21,
    0x20,
    0xe0,
    0xa0,
    0x60,
    0x61,
    0xa1,
    0x63,
    0xa3,
    0xa2,
    0x62,
    0x66,
    0xa6,
    0xa7,
    0x67,
    0xa5,
    0x65,
    0x64,
    0xa4,
    0x6c,
    0xac,
    0xad,
    0x6d,
    0xaf,
    0x6f,
    0x6e,
    0xae,
    0xaa,
    0x6a,
    0x6b,
    0xab,
    0x69,
    0xa9,
    0xa8,
    0x68,
    0x78,
    0xb8,
    0xb9,
    0x79,
    0xbb,
    0x7b,
    0x7a,
    0xba,
    0xbe,
    0x7e,
    0x7f,
    0xbf,
    0x7d,
    0xbd,
    0xbc,
    0x7c,
    0xb4,
    0x74,
    0x75,
    0xb5,
    0x77,
    0xb7,
    0xb6,
    0x76,
    0x72,
    0xb2,
    0xb3,
    0x73,
    0xb1,
    0x71,
    0x70,
    0xb0,
    0x50,
    0x90,
    0x91,
    0x51,
    0x93,
    0x53,
    0x52,
    0x92,
    0x96,
    0x56,
    0x57,
    0x97,
    0x55,
    0x95,
    0x94,
    0x54,
    0x9c,
    0x5c,
    0x5d,
    0x9d,
    0x5f,
    0x9f,
    0x9e,
    0x5e,
    0x5a,
    0x9a,
    0x9b,
    0x5b,
    0x99,
    0x59,
    0x58,
    0x98,
    0x88,
    0x48,
    0x49,
    0x89,
    0x4b,
    0x8b,
    0x8a,
    0x4a,
    0x4e,
    0x8e,
    0x8f,
    0x4f,
    0x8d,
    0x4d,
    0x4c,
    0x8c,
    0x44,
    0x84,
    0x85,
    0x45,
    0x87,
    0x47,
    0x46,
    0x86,
    0x82,
    0x42,
    0x43,
    0x83,
    0x41,
    0x81,
    0x80,
    0x40
  );
  let crcHi = 0xff;
  let crcLo = 0xff;
  for (let i = 0; i < data.length / 2; i++) {
    const crcIndex = crcHi ^ buffer[i];
    crcHi = crcLo ^ auchCRCHi[crcIndex];
    crcLo = auchCRCLo[crcIndex];
  }
  return decToHex((crcHi << 8) | crcLo, 2);
}

/**
 * 10进制转16进制
 * @param number 数据
 * @param byteLen 数据
 */
function decToHex(number, byteLen) {
  if (byteLen < 1) {
    return -1;
  }
  number = number.toString(16);
  for (let i = 2 * byteLen - number.length; i > 0; i--) {
    number = '0' + number;
  }
  number = number.toUpperCase();
  return number;
}

/**
 * 十六进制字符串转字节数组
 * 每2个字符串转换
 * 903132333435363738 转为 [-112, 49, 50, 51, 52, 53, 54, 55, 56]
 * @param {String} str 符合16进制字符串
 */
function str2Bytes(str) {
  let pos = 0;
  let len = str.length;
  if (len % 2 != 0) {
    return null;
  }
  len /= 2;
  let hexA = new Array();
  for (let i = 0; i < len; i++) {
    let s = str.substr(pos, 2);
    let v = parseInt(s, 16);
    if (v >= 127) v = v - 255 - 1;
    hexA.push(v);
    pos += 2;
  }
  return hexA;
}

/**
 * 字节数组转十六进制字符串
 * [-112, 49, 50, 51, 52, 53, 54, 55, 56] 转换 903132333435363738
 * @param {Array} arr 符合16进制数组
 */
function bytes2Str(arr) {
  let str = '';
  for (let i = 0; i < arr.length; i++) {
    let tmp;
    const num = arr[i];
    if (num < 0) {
      //此处填坑，当byte因为符合位导致数值为负时候，需要对数据进行处理
      tmp = (255 + num + 1).toString(16);
    } else {
      tmp = num.toString(16);
    }
    if (tmp.length == 1) {
      tmp = '0' + tmp;
    }
    str += tmp;
  }
  return str;
}

// Buffer 对象转二进制
function bytesToBinary(bytes) {
  const length = bytes.length;
  let result = '';
  for (let i = 0; i < length; i++) {
    const binStr = Number(bytes[i]).toString(2);
    result += '0'.repeat(8 - binStr.length) + binStr; // 不足八位前置补0
  }
  return result.toString();
}

/**
 * 十六进制字符串转数组
 * 1185759ac35a91143f97037002b1a266 转换 ["11", "85", "75", "9a", "c3", "5a", "91", "14", "3f", "97", "03", "70", "02", "b1", "a2", "66"]
 * @param {String} str 十六进制字符串
 */
function str2Arr(str) {
  let pos = 0;
  let len = str.length;
  if (len % 2 != 0) {
    return null;
  }
  len /= 2;
  const hexA = new Array();
  for (let i = 0; i < len; i++) {
    let s = str.substr(pos, 2);
    hexA.push(s);
    pos += 2;
  }
  return hexA;
}

/**
 * 十六进制数组进行异或
 * 15 转 0F
 * @param {Number} num 十进制数字
 */
function bytesDes(arr) {
  let des = 0;
  for (let i = 0; i < arr.length; i++) {
    des ^= parseInt(arr[i], 16);
  }
  return des.toString(16).toUpperCase();
}

/**
 * 十进制转十六进制
 * @param {Array} arr 十六进制数组
 */
function toHex(num) {
  return ('0' + Number(num).toString(16)).slice(-2).toUpperCase();
}

/**
 * 十六进制转十进制
 * @param  value 十六进制
 */
function ex16hex(value) {
  value = filterSpecialCharacter(value);
  value = value.replace('0x', '');
  let arr = value.split('');
  arr = arr.reverse();
  // var len = arr.length;
  let res = 0;
  eachArr(arr, (i, v) => {
    // console.log(v, i);
    const num = hexChange(v);
    // console.log(num);
    res += muti16(num, i);
  });
  return res;
}

function eachArr(object, callback) {
  const type = (function () {
    switch (object.constructor) {
      case Object:
        return 'Object';
      case Array:
        return 'Array';
      case NodeList:
        return 'NodeList';
      default:
        return 'null';
    }
  })();
  // 为数组或类数组时, 返回: index, value
  if (type === 'Array' || type === 'NodeList') {
    // 由于存在类数组NodeList, 所以不能直接调用every方法
    [].every.call(object, function (v, i) {
      return callback.call(v, i, v) === false ? false : true;
    });
  }
  // 为对象格式时,返回:key, value
  else if (type === 'Object') {
    for (const i in object) {
      if (callback.call(object[i], i, object[i]) === false) {
        break;
      }
    }
  }
}

// 字符转16进制数字
function hexChange(v) {
  let res;
  switch (v) {
    case 'A':
      res = 10;
      break;
    case 'B':
      res = 11;
      break;
    case 'C':
      res = 12;
      break;
    case 'D':
      res = 13;
      break;
    case 'E':
      res = 14;
      break;
    case 'F':
      res = 15;
      break;
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      res = Number(v);
      break;
    default:
      res = 0;
      break;
  }
  return res;
}

// 过滤所有特殊字符
function filterSpecialCharacter(s) {
  let pattern = new RegExp(
    "[`~!@#$^&*()=|{}':;',\\[\\].<>/?~！@#￥……&*（）——|{}【】‘；：”“'。，、？↵\r\n]"
  );
  let rs = '';
  for (let i = 0; i < s.length; i++) {
    rs = rs + s.substr(i, 1).replace(pattern, '');
  }
  return rs;
}

// 返回 v 乘以 n 个 16 的积
function muti16(v, n) {
  let temp = v;
  for (let i = 0; i < n; i++) {
    temp *= 16;
  }
  return temp;
}

/**
 * 二进制转十六进制
 * 00001001 转 09
 * @param {String} str 二进制字符串
 */
function binTohex(str) {
  const hex_array = [
    { key: 0, val: '0000' },
    { key: 1, val: '0001' },
    { key: 2, val: '0010' },
    { key: 3, val: '0011' },
    { key: 4, val: '0100' },
    { key: 5, val: '0101' },
    { key: 6, val: '0110' },
    { key: 7, val: '0111' },
    { key: 8, val: '1000' },
    { key: 9, val: '1001' },
    { key: 'a', val: '1010' },
    { key: 'b', val: '1011' },
    { key: 'c', val: '1100' },
    { key: 'd', val: '1101' },
    { key: 'e', val: '1110' },
    { key: 'f', val: '1111' },
  ];
  let value = '';
  let list = [];
  if (str.length % 4 !== 0) {
    let a = '0000';
    let b = a.substring(0, 4 - (str.length % 4));
    str = b.concat(str);
  }
  while (str.length > 4) {
    list.push(str.substring(0, 4));
    str = str.substring(4);
  }
  list.push(str);
  for (let i = 0; i < list.length; i++) {
    for (let j = 0; j < hex_array.length; j++) {
      if (list[i] == hex_array[j].val) {
        value = value.concat(hex_array[j].key);
        break;
      }
    }
  }
  return value;
}

/**
 * 十六进制数字和ASCII字符之间转换
  * var hex="0x10";//十六进制
var charValue = String.fromCharCode(hex);//生成Unicode字符
var charCode = charValue.charCodeAt(0);//获取指定字符的十进制表示.
var hexOri="0x"+charCode.toString(16);;//将int值转换为十六进制
   *
 */

// hex:0x10
// charValue:+
// charCode:16
// hexOri:0x10

/**
 * 十六进制数组转ASCLL编码
  * var hex="0x10";//十六进制
var charValue = String.fromCharCode(hex);//生成Unicode字符
var charCode = charValue.charCodeAt(0);//获取指定字符的十进制表示.
var hexOri="0x"+charCode.toString(16);;//将int值转换为十六进制
   *
 */
// function HexToAscll(str) {
//   let pos = 0;
//   let len = str.length/2;
//   let hexA = '';
//   for (let i = 0; i < len; i++) {
//     let s = str.substr(pos, 2);
//     hexA += String.fromCharCode(`0x${s}`);
//     pos += 2;
//   }
//   return hexA;
// }

/**
 *  十六进制浮点数转换十进制浮点数
 * @param data 十六进制浮点数
 */
function analyze(data) {
  let str2 = parseInt(data, 16).toString(2);
  while (str2.length < 32) {
    str2 = '0' + str2;
  }
  const sign = str2.substring(0, 1);
  const exponent = str2.substring(1, 9);
  const expint = parseInt(exponent, 2);
  const mobit = expint - 127;
  const d = Math.pow(2, mobit);
  const last = str2.substring(9);
  let lastRes = 0; // 存放尾数的结果
  for (let i = 0; i < last.length; i++) {
    const b = last[i];
    if (b === '1') {
      lastRes += 1 / Math.pow(2, i + 1); // 尾数的计算
    }
  }
  const result = d * (sign === '1' ? -1 : 1) * (1 + lastRes);
  return result;
}

/**
 *  十进制浮点数转换成十六进制浮点数
 * @param data 十进制浮点数
 */
function toHexFloat(data) {
  const dataArr = data.split('.');
  let header; //整数部分
  let footer; //小数部分
  header = dataArr[0] !== '0' ? dataArr[0] : 0;
  footer = dataArr[1] !== '0' ? dataArr[1] : 0;
  if (header == '0' && footer == '0') {
    return '00000000';
  }
  let SS = parseFloat(data) >= 0 ? 0 : 1; //S,符号位
  header = Math.abs(header);
  let arr1 = []; //二进制整数
  let arr2 = []; //二进制小数
  if (header !== 0) {
    let i = 0,
      a;
    header = parseInt(header);
    while (true) {
      a = header % 2;
      arr1[i] = a;
      i++;
      header = Math.floor(header / 2);
      if (header == 0) {
        break;
      }
    }
  } else {
    arr1[0] = header;
  }
  if (footer !== 0) {
    let a,
      b,
      j = 0;
    while (true) {
      footer = '0.' + footer;
      a = (footer * 2).toString();
      b = a.split('.');
      arr2[j] = b[0];
      footer = b[1];
      j++;
      if (j >= 10) {
        break;
      }
    }
  } else {
    arr2[0] = 0;
  }
  arr1 = arr1.reverse(); //整数逆取，小数顺取
  let arr1str = '',
    arr2str = '';
  arr1.forEach((value) => (arr1str += value));
  arr2.forEach((value) => (arr2str += value));
  let obstr;
  if (arr1str.length > 1) {
    obstr = arr1str.substr(0, 1) + '.' + arr1str.substr(1, arr1str.length) + arr2str;
  } else {
    obstr = arr1str.substr(0, 1) + '.' + arr2str;
  }
  // console.log(obstr);
  let EE = arr1str.length - 1 + 127; //E
  EE = EE.toString(2).padStart(8, '0'); //8位
  const MM = obstr.substr(2, obstr.length).padEnd(23, '0'); //M
  const bin = SS + EE + MM;
  const endHex = parseInt(bin, 2).toString(16);
  // console.log(EE, MM, bin, endHex);
  return endHex;
}

/**
 *  ByteLength
 * @param type 类型
 */
function getByteLength(type) {
  switch (String(type).toLowerCase()) {
    case 'int':
    case 'int8':
    case 'int16':
    case 'uint16':
    case 'enum':
    case 'bool':
    case 'bit16':
      return 2;
    case 'int32':
    case 'uint32':
    case 'float':
    case 'real':
    case 'bit32':
      return 4;
    case 'int64':
    case 'uint64':
    case 'double':
      return 8;
    default:
      throw new Error('Unsupported type');
  }
}

/**
 *  bufferize
 * @param data 数据
 * @param type 类型
 */
function bufferize(data, type) {
  if (Array.isArray(data) === false) {
    data = [data];
  }

  const quantity = data.length;
  const byteLength = getByteLength(type);
  const size = quantity * byteLength;
  const buffer = Buffer.alloc(size);

  for (let i = 0; i < quantity; i++) {
    if (type === 'int') {
      buffer.writeIntBE(data[i], i * byteLength, 2);
    } else if (type === 'int8') {
      buffer.writeInt8(data[i], i * byteLength);
    } else if (type === 'int16') {
      buffer.writeInt16BE(data[i], i * byteLength);
    } else if (type === 'uint16') {
      buffer.writeUInt16BE(data[i], i * byteLength);
    } else if (type === 'int32') {
      buffer.writeInt32BE(data[i], i * byteLength);
    } else if (type === 'uint32') {
      buffer.writeUInt32BE(data[i], i * byteLength);
    } else if (type === 'float' || type === 'real') {
      buffer.writeFloatBE(data[i], i * byteLength);
    } else if (type === 'double') {
      buffer.writeDoubleBE(data[i], i * byteLength);
    } else if (type === 'bit16') {
      buffer.write(data[i].join(','), i * byteLength, 16);
    } else if (type === 'bit32') {
      buffer.write(data[i].join(','), i * byteLength, 32);
    }
  }

  return buffer;
}

/**
 *  unbufferize
 * @param buffer 数据
 * @param type 类型
 */
function unbufferize(buffer, type) {
  const byteLength = getByteLength(type); // 数组
  const quantity = buffer.length / byteLength; // 数组个数
  const data = [];

  for (let i = 0; i < quantity; i++) {
    if (type === 'int') {
      // 2
      data.push(buffer.readIntBE(i * byteLength));
    } else if (type === 'int8') {
      // 2
      data.push(buffer.readInt8(i * byteLength));
    } else if (type === 'int16') {
      // 2
      data.push(buffer.readInt16BE(i * byteLength));
    } else if (type === 'uint16') {
      // 2

      data.push(buffer.readUInt16BE(i * byteLength));
    } else if (type === 'int32') {
      // 4
      // 0000 0000
      // uint64 8
      data.push(buffer.readInt32BE(i * byteLength));
    } else if (type === 'uint32') {
      // 4
      // uint64 8
      data.push(buffer.readUInt32BE(i * byteLength));
    } else if (type === 'float' || type === 'real') {
      // 4
      data.push(buffer.readFloatBE(i * byteLength));
    } else if (type === 'double') {
      data.push(buffer.readDoubleBE(i * byteLength));
    } else if (type === 'bit16') {
      data.push(buffer.slice(i * byteLength, 16));
    } else if (type === 'bit32') {
      data.push(buffer.slice(i * byteLength, 32));
    }
  }

  return data;
}

/**
 * hexCase 转成报文
 * @param str id
 */
function hexCase(str) {
  const bus = str.toString('hex').toUpperCase();
  return bus;
}

/**
 * ReadCoils 0x01读输出线圈
 * @param id id
 * @param addr 地址起始点
 * @param readLen 线圈数量
 */
function readCoils(id, addr, readLen) {
  const bus =
    id + '01' + addr + decToHex(readLen, 2) + crc16Modbus(id + '01' + addr + decToHex(readLen, 2));
  return bus;
}

/**
 * ReadDiscreteInputs 0x02读输入线圈
 * @param id id
 * @param addr 地址起始点
 * @param readLen 线圈数量
 */
function readDiscreteInputs(id, addr, readLen) {
  const bus =
    id + '02' + addr + decToHex(readLen, 2) + crc16Modbus(id + '02' + addr + decToHex(readLen, 2));
  return bus;
}

/**
 * ReadHoldRegister 0x03读保持寄存器
 * @param id id
 * @param addr 地址起始点
 * @param readLen 长度
 */
function readHoldRegister(id, addr, readLen) {
  const bus =
    id + '03' + addr + decToHex(readLen, 2) + crc16Modbus(id + '03' + addr + decToHex(readLen, 2));
  return bus;
}

/**
 * ReadInputRegisters 0x04读输入寄存器
 * @param id id
 * @param addr 地址起始点
 * @param readLen 长度
 */
function readInputRegisters(id, addr, readLen) {
  const bus =
    id + '04' + addr + decToHex(readLen, 2) + crc16Modbus(id + '04' + addr + decToHex(readLen, 2));
  return bus;
}

/**
 * WriteCoil 0x05写单个输出线圈
 * @param id id
 * @param addr 线圈地址
 * @param binary 线圈状态 0xFF00表示开启，0x0000表示关闭
 */
function writeCoil(id, addr, binary) {
  const bus = id + '05' + addr + binary + crc16Modbus(id + '05' + addr + binary);
  return bus;
}

function decimalPlaces(val) {
  let decimal = 0;
  if (val !== 0) {
    let zero = '';
    for (let i = 0; i < val; i++) {
      zero += '0';
    }
    decimal = Number(1 + zero);
  }
  return decimal;
}

/**
 * WriteRegister 0x06写单个保持寄存器
 * @param id id
 * @param addr 寄存器地址
 * @param valueObj 寄存器值 {value: 16,type:'float',leng , decimalPlaces} -> 0010
 */
function writeRegister(id, addr, valueObj) {
  const { value, type, byteOrder } = valueObj;

  if (type === 'bit' || type === 'bitOne') {
    // const buffer = Buffer.from(value.join(''), 'binary');

    let newVal = value;

    if (byteOrder === 'CDAB') {
      newVal = value.reverse();
    }

    const b = binTohex(newVal.join(''));

    // 将 Buffer 对象转换为十六进制字符串
    // const b = buffer.toString('hex');

    const bus = id + '06' + addr + b + crc16Modbus(id + '06' + addr + b);

    return bus;
  } else {
    let num = value;

    // if(type)

    let decimal = 0;

    if (valueObj.decimalPlaces !== 0) {
      decimal = decimalPlaces(valueObj.decimalPlaces);
      num = num * decimal;
    }
    const a = bufferize(num, valueObj.type);

    const b = a.toString('hex');
    const bus = id + '06' + addr + b + crc16Modbus(id + '06' + addr + b);

    return bus;
  }
}

/**
 * WriteRegisters 0x10 多个保持寄存器
 * @param id id
 * @param addr 寄存器起始地址
 * @param addrNum 寄存器数量
 * @param regByte 寄存器字节
 * @param valueAry 寄存器值[{value: 16.01,type:'float',leng , decimalPlaces},{value: 17,type:'int16'},] ->[00100001,0011,0012]->0010 0001 0011 0012
 */
function writeRegisters(id, addr, addrNum, regByte, valueAry) {
  let arrValue = '';
  // console.log('valueAry', valueAry);
  valueAry.forEach((item) => {
    let num = item.value;
    switch (item.computedType) {
      case 'plus':
        num = (num - item.computedNum).toFixed(item.decimalPlaces || 0);
        break;
      case 'reduce':
        num = (num + item.computedNum).toFixed(item.decimalPlaces || 0);
        break;
      case 'ride':
        num = num / item.computedNum;
        // num.toFixed(4);
        // (
        //           Math.round(item.value * item.computedNum * 100) / 100
        //         ).toFixed(2);
        break;
      case 'except':
        num = (num * item.computedNum).toFixed(item.decimalPlaces || 0);
        break;
      default:
        break;
    }
    let decimal = 0;
    if (item.decimalPlaces && item.decimalPlaces !== 0) {
      decimal = decimalPlaces(item.decimalPlaces);
      num = num * decimal;
    }
    let buffer = bufferize(num, item.type);
    if (item.byteOrder) {
      let newBuf = byteOrderType(buffer.slice(0, item.leng), 'ABCD', item.byteOrder);
      buffer = newBuf;
    }
    const b = buffer.toString('hex');
    arrValue += b;
  });

  const bus =
    id +
    '10' +
    addr +
    decToHex(addrNum, 2) +
    decToHex(regByte, 1) +
    arrValue +
    crc16Modbus(id + '10' + addr + decToHex(addrNum, 2) + decToHex(regByte, 1) + arrValue);
  return bus;
}

/**
 * WriteCoils 0x0F 写多个输出线圈
 * @param id id
 * @param addr 寄存器地址
 * @param valueObj 寄存器值 {number: 10, value:10}-> 0010 线圈数量 + *字节数（线圈/8 进1） + 线圈状态
 */
function writeCoils(id, addr, valueObj) {
  const bus =
    id +
    '0F' +
    addr +
    decToHex(valueObj.number, 2) +
    decToHex(Math.ceil(valueObj.number / 8), 1) +
    decToHex(valueObj.value, Math.ceil(valueObj.number / 8)) +
    crc16Modbus(
      id +
        '0F' +
        addr +
        decToHex(valueObj.number, 2) +
        decToHex(Math.ceil(valueObj.number / 8), 1) +
        decToHex(valueObj.value, Math.ceil(valueObj.number / 8))
    );
  return bus;
}

/**
 * send
 * @param fc 01 02 03 04 05 06 10 15=0F
 * @param unit 从机地址
 * @param address 寄存器起始地址
 * @param arg 长度
 */
function send(fc, unit, address, arg) {
  switch (fc) {
    case 1:
      return readCoils(unit, address, arg);
    case 2:
      return readDiscreteInputs(unit, address, arg);
    case 3:
      return readHoldRegister(unit, address, arg);
    case 4:
      return readHoldRegister(unit, address, arg);
    case 5:
      return writeCoil(unit, address, arg);
    case 6:
      return writeRegister(unit, address, arg);
    case 10:
      return writeRegisters(
        unit,
        address,
        arg['addrNum'],
        arg['regByte'],
        arg['valueArr']
      );
    case 15:
      // 0F
      return writeCoils(unit, address, arg);
  }
  return Promise.reject(new Error('Unknown fc code'));
}

/**
 * byteOrderType 字节序
 * @param strType 原报文字节序 ABCD
 * @param str 原报文 01 02 03 04
 * @param strNewType 新报文字节序
 */
function byteOrderType(str, strType, strNewType) {
  let oldstr = hexCase(str);
  let newbufAry = str2Arr(oldstr);
  let strJson = {};
  for (const key in strType) {
    strJson[strType[key]] = newbufAry[key];
  }
  let newStr = '';
  for (const key in strNewType) {
    newStr += strJson[strNewType[key]];
  }
  let newBuf = Buffer.alloc(strNewType.length);
  newBuf.write(newStr, 'hex');
  return newBuf;
}

const sendToTcp = (data = null) => {
  if (data) {
    const newData = data.slice(0, data.length - 4);

    const dataLength = decToHex(newData.length / 2, 2);

    const mbap = randomStringFn(4, 'ABCDEF') + '0000' + dataLength;

    return `${mbap}${newData}`;
  }

  return data;
};

module.exports = {
  randomStringFn,
  crc16Modbus,
  decToHex,
  str2Bytes,
  bytes2Str,
  str2Arr,
  bytesDes,
  toHex,
  ex16hex,
  eachArr,
  hexChange,
  filterSpecialCharacter,
  muti16,
  binTohex,
  analyze,
  toHexFloat,
  getByteLength,
  bufferize,
  unbufferize,
  hexCase,
  readCoils,
  readDiscreteInputs,
  readHoldRegister,
  readInputRegisters,
  writeCoil,
  decimalPlaces,
  writeRegister,
  writeRegisters,
  writeCoils,
  send,
  byteOrderType,
  sendToTcp,
  bytesToBinary
};
